package com.shujia

import java.io.{BufferedReader, FileReader}
import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}
import java.util.HashMap
import scala.io.{BufferedSource, Source, StdIn}

object Demo02Base {
  // Scala基础语法
  // 这是单行注释
  /*
  这是多行注释
  这是多行注释
  这是多行注释
  这是多行注释
  ......
   */

  /**
   * 这是文档注释
   * 这是文档注释
   * 这是文档注释
   * 这是文档注释
   * ......
   */
  def main(args: Array[String]): Unit = {
    println("Scala输出")

    // 定义变量
    /**
     * 变量的命名规则：
     * 1、必须是大小写字母、数字、下划线构成
     * 2、不能以数字开头
     * 3、不能是关键字
     *
     * 在Scala定义变量不需要声明类型
     * 整体的变量可以分为两类：变量、常量
     * 由var修饰的即为变量
     * 由val修饰的即为常量
     */
    // 定义一个int整形的变量i，赋值10
    var i = 10
    i = 100 // 由var修饰的变量可以重新赋值
    //    i = 1.0 // 虽然可以重新赋值，但类型得匹配，因为i在声明时类型就固定了，虽然可以进行类型推导，但只会进行一次推导
    // 定义时可以不指定类型，会自动进行推断，也可以手动进行声明
    var ii: Int = 10 // 手动声明类型后 就不会进行推导了
    println(ii)
    ii = 1000

    // 定义一个double浮点类型的常量d，赋值1.0
    val d = 1.0
    //    d = 2.0 // 由val修饰的变量即为常量，不能再进行二次赋值

    //    Demo01HelloWorldJava xxx = new Demo01HelloWorldJava() // Java接收类的对象的语法
    val xxx = new Demo01HelloWorldJava()

    // Scala中所有的数据都有共同的祖先，都属于Any类型
    // Any类型又可以分为两类：AnyVal数值类型、AnyRef引用类型
    // AnyVal数值类型又可以分为8类：Byte、Short、Int、Long、Float、Double、Boolean、Char
    val byte: Byte = 12
    val short: Short = 20
    val int: Int = 10
    val long: Long = 10L

    val float: Float = 1.234F
    val double: Double = 1.234D

    val char: Char = 'a'
    println(char)
    val char2Int: Int = char
    println(char2Int)

    val boolean1: Boolean = true
    val boolean2: Boolean = false

    // AnyRef指各种各样的类

    // 类型转换
    // 在Scala中如果某个数值类型（AnyVal的子类型）需要转成另外一种数值类型，则直接to类型即可
    // 将Int类型转换成其他类型
    val i2: Int = 20
    val int2Byte: Byte = i2.toByte
    val int2Short: Short = i2.toShort
    val int2Long1: Long = i2 // 低精度转高精度 可以自动转换 隐式类型转换
    val int2Long2: Long = i2.toLong // 显示转换 强制转换 手动转换
    val int2Float: Float = i2.toFloat
    val int2Double: Double = i2.toDouble
    val int2Char: Char = i2.toChar

    // Boolean类型无法和其他数值类型进行转换

    // 字符串类型
    val s1: String = "123" // Scala中的String类型来源于Java中的String类型，但做了改进与加强
    println(s1.toInt) // toInt转换类型这类操作是Scala独有的，实际上是通过隐式转换加上去的
    println(s1.toDouble) // 隐式转换 就是动态给对象增加方法
    println(s1.toByte)

    // 字符串常见的操作
    // 转大小写、切分、按照位置截取字符串等 这些操作是从Java中获得的
    val s2 = "abcd,EFgH,ijk,lMN"
    print(s2.toLowerCase())
    print(s2.toUpperCase())
    println(s2.substring(5, 9)) // 按照位置截取字符串
    // Array[String] : 表示Scala中的字符串数组 相当于Java中的 String [] splits
    val splits: Array[String] = s2.split(",")
    // 数组一般可以遍历，可以通过下标取元素
    println(splits(1)) // 在Java中是通过[]中括号传入下标的，在Scala中是通过()传入的
    for (elem <- splits) { // 以类似Java增强for的形式进行遍历
      println(elem)
    }
    // String类型在Scala中比较常用的特有的方法
    splits.foreach(println) // 以函数式编程的思想进行遍历
    println(s2.head)
    println(s2.tail)
    println(s2.take(3))
    println(s2.length)
    println(s2.size)
    println(s2.sorted)
    println(s2.isEmpty)
    // map、filter、flatMap、groupBy、sortBy、sortWith等.....

    // Scala中的println方法实际上是来源于Java的System.out.println，只不过作了简化
    // 类似的方法：print、printf
    print("不换行输出")
    print("不换行输出")
    println()

    // 格式化字符串：需要将字符串按照固定的格式进行输出
    // 例如：9 9 乘法表中每一个表达式都是，数 * 数 = 数 这样的格式
    printf("%d * %d = %d\n", 9, 9, 81) // 9 * 9 = 81 类似c语言
    // 还可以通过引用变量的方式进行传值
    val num01: Int = 9
    val num02: Int = 9
    val num03: Int = 81
    val _num04: Int = 81
    println(s"$num01 * $num02 = $num03")
    println(s"$num01 * $num02 = ${_num04}") // 如果需要引用的变量名以下划线开头，则需要用花括号括起来

    // 如果字符串需要换行，例如写SQL的时候，则可以使用""""""
    val sqlStr: String = "select clazz,count(*) as cnt from student group by clazz"
    val sqlStr2: String =
      """
        |select clazz
        |       ,count(*) as cnt
        |from student
        |group by clazz
        |""".stripMargin // 适合写SQL

    println(sqlStr)
    println(sqlStr2)

    // 算数运算符
    println(10 + 3) // 13
    println(10 - 3) // 7
    println(10 * 3) // 30
    println(10 / 3) // 3
    println(10 % 3) // 1

    // 关系（比较）运算符
    println(3 > 4)
    println(3 < 4)
    println(3 >= 4)
    println(3 <= 4)
    println(3 != 4)
    println(3 == 4)

    println("a" > "b") // false
    println("上" > "下") // false
    println("六" > "四") // false

    val str1: String = "abc"
    val str2: String = new String("abc")
    val str3: String = "abc"

    println(str1 == str2) // true 在Scala中是比较值是否相等 相当于equals
    println(str1 == str3) // true

    println(str1.equals(str2))
    println(str1.equals(str3))

    println(str1.eq(str2)) // false 相当于与Java的直接比较 ==
    println(str1.eq(str3)) // true

    // 位运算符
    println(7 & 4) // 4
    println(7 | 4) // 7
    println(7 ^ 4) // 3
    println(7 << 2) // 28
    println(7 >> 2) // 1
    println(~7) // -8

    // 逻辑运算符
    println(true && false)
    println(true || false)
    println(true ^ false)
    println(!false)

    val i3: Int = 1
    val i4: Int = i3.+(2)
    println(i4)

    // 省略形式
    val i5: Int = i3 + 2

    println(s2.split(",").toList)
    println("=======================================")
    println(s2 split "," toList) // 不推荐这种写法
    println("=" * 30)

    // 流程控制之选择结构
    // 输入一个年龄age，如果小于18则输出 童年
    //    println("请输入一个年龄：")
    //    var age: Int = StdIn.readInt()
    var age: Int = 10
    if (age < 18) {
      println("童年")
    }

    // 输入一个年龄age，如果小于18则输出童年，如果大于等于18则输出成年
    //    println("请输入一个年龄：")
    //    age = StdIn.readInt()
    age = 20
    if (age < 18) {
      println("童年")
    } else {
      println("成年")
    }

    // 输入一个年龄age，如果age小于0或者大于200则输出年龄异常
    // ，如果小于18则输出童年，如果大于等于18则输出成年
    //    println("请输入一个年龄：")
    //    age = StdIn.readInt()
    age = -10
    if (age < 0 || age > 200) {
      println("年龄异常")
      println("年龄异常")
      println("年龄异常")
    } else if (age < 18) {
      println("童年")
    } else {
      println("成年")
    }

    // Scala中类似Java的switch，一般适用于等值比较
    val v1: Int = 10

    // Scala中的match可以有返回值
    val returnVal: Int = v1 match {
      case 10 =>
        println("值为10")
        println("值为10")
        println("值为10")
        println("值为10")
        // 不需要break 多行代码也不需要使用{}
        //        return 10 // 如果不是在函数内部进行返回 不能使用return 否则会导致程序直接结束
        10 // Scala会默认以最后一行代码最为返回值，return关键字需要省略
      case 20 =>
        println("值为20")
        20
      case _ =>
        // 相当于Java中的default
        println("其他情况")
        0
    }

    println("模式匹配以及结束")
    println(returnVal)

    // 循环结构：for循环、while循环
    // for循环主要用于遍历数据容器 类似Java中的增强for
    val strArr: Array[String] = "java,scala,python".split(",")
    for (elem <- strArr) {
      println(elem)
    }

    // scala中是没有for i的形式
    // 如果需要使用下标进行遍历，可以使用 to、until、range方法
    for (i <- 0 to strArr.length - 1) {
      println(strArr(i))
    }

    for (i <- 0 until strArr.length) {
      println(strArr(i))
    }

    for (i <- Range(0, strArr.length, 1)) {
      println(strArr(i))
    }

    // while循环专门用于实现重复的代码逻辑即循环
    // 计算1~100的和
    var cnt: Int = 1
    var sum: Int = 0
    while (cnt <= 100) {
      sum += cnt
      cnt += 1
    }
    println(sum)

    // do while
    var cnt2: Int = 1
    var sum2: Int = 0
    do {
      sum2 += cnt2
      cnt2 += 1
    } while (cnt2 <= 100)
    println(sum)

    // 以Java的方式读文件
    // 1、读取文件
    val br: BufferedReader = new BufferedReader(new FileReader("scala/data/words.txt"))
    var line: String = br.readLine
    // 2、通过循环读取每一条数据
    while (line != null) { // 3、对每一行数据进行切分 取出每一个单词
      println(line)
      line = br.readLine()
    }
    // 关闭文件
    br.close()

    println("=" * 30)


    // 以Scala的方式读取文件
    // 黄颜色标记 是因为文件没有关闭
    Source
      .fromFile("scala/data/words.txt")
      .getLines()
      .foreach(println)
    println("=" * 30)
    // 最好在fromFile之后进行接收 方便关闭文件
    val source: BufferedSource = Source
      .fromFile("scala/data/words.txt")

    source
      .getLines()
      .foreach(println)
    println("=" * 30)
    // 以字符流的方式进行遍历文件
    val source2: BufferedSource = Source
      .fromFile("scala/data/words.txt")
    val buffered: BufferedIterator[Char] = source2.buffered
    while (buffered.hasNext) {
      print(buffered.next())
    }

    source2.close()
    println("=" * 30)

    // 当文件比较小时，可以将整个文件的所有内容作为一个字符串返回
    val source3: BufferedSource = Source
      .fromFile("scala/data/words.txt")

    val wordsStr: String = source3.mkString
    println(wordsStr)

    source3.close()

    // 读取MySQL的数据，Scala中没有特殊的方式，还需要借助Java中的JDBC进行连接操作
    // 1、建立连接
    val conn: Connection = DriverManager.getConnection("jdbc:mysql://rm-bp1h7v927zia3t8iwho.mysql.rds.aliyuncs.com:3307/stu023", "shujia23", "123456")
    // 基于班级 获取某个班级的学生数据
    val clazz: String = "文科四班"
    // 2、创建Statement
    val pSt: PreparedStatement = conn.prepareStatement("select id,name,age,gender,clazz from student where clazz = ?")

    // 3、设置参数
    pSt.setString(1, clazz)

    // 4、执行SQL
    val rs: ResultSet = pSt.executeQuery()

    // 5、通过循环遍历
    while (rs.next()) {
      val id: String = rs.getString("id")
      val name: String = rs.getString("name")
      val age: String = rs.getString("age")
      val gender: String = rs.getString("gender")
      val clazz: String = rs.getString("clazz")
      println(s"$id,$name,$age,$gender,$clazz")
    }
    // 关闭连接
    rs.close()
    pSt.close()
    conn.close()


  }
}
